

/*
 * Copyright (c) 2021. Dylan Cai
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

@file:Suppress("unused")
package com.gitee.wsl.android.ui.ext

//package com.dylanc.longan

import android.graphics.Color
import android.graphics.Paint
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.os.CountDownTimer
import android.text.method.HideReturnsTransformationMethod
import android.text.method.PasswordTransformationMethod
import android.util.Size
import android.widget.CheckBox
import android.widget.TextView
import androidx.annotation.FontRes
import androidx.core.text.PrecomputedTextCompat
import androidx.core.widget.TextViewCompat
import androidx.core.widget.doAfterTextChanged
import androidx.lifecycle.LifecycleOwner
import com.gitee.wsl.ext.K
import com.gitee.wsl.ext.noGetter
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.lang.ref.WeakReference
import kotlin.math.roundToInt
import android.animation.LayoutTransition
import android.os.Handler
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.TextPaint
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.text.style.ForegroundColorSpan
import android.text.style.UnderlineSpan
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import androidx.core.text.toSpannable
import com.gitee.wsl.ext.coroutines.doOnLifecycle
import com.gitee.wsl.ext.runOnScope


object TextViewExt{
    fun setCompoundDrawables(
        textView:TextView,
        drawableSize: Size?=null,
        drawableLeft: Drawable?,
        drawableRight: Drawable?,
        drawableTop: Drawable?=null,
        drawableBottom: Drawable?=null,
        isRtl:Boolean=false
    ) {
        drawableSize?.let {
            drawableLeft?.setBounds(0, 0, it.width, it.height)
            drawableRight?.setBounds(0, 0,it.width, it.height)
            drawableTop?.setBounds(0, 0, it.width, it.height)
            drawableBottom?.setBounds(0, 0, it.width, it.height)
        }
        //setDrawable
        textView.setCompoundDrawables(
            if (isRtl) drawableRight else drawableLeft,
            drawableTop,
            if (isRtl) drawableLeft else drawableRight,
            drawableBottom
        )
    }
}

inline var TextView.isFakeBoldText: Boolean
    get() = paint.isFakeBoldText
    set(value) {
        paint.isFakeBoldText = value
    }

var TextView.font: Int
    get() = noGetter()
    set(@FontRes value) {
        typeface = context.getCompatFont(value)
    }

var TextView.typefaceFromAssets: String
    get() = noGetter()
    set(value) {
        typeface = Typeface.createFromAsset(context.assets, value)
    }


inline val TextView.textString: String get() = text.toString()

fun TextView.isTextEmpty(): Boolean = textString.isEmpty()

fun TextView.isTextNotEmpty(): Boolean = textString.isNotEmpty()

inline var TextView.isPasswordVisible: Boolean
    get() = transformationMethod != PasswordTransformationMethod.getInstance()
    set(value) {
        transformationMethod = if (value) {
            HideReturnsTransformationMethod.getInstance()
        } else {
            PasswordTransformationMethod.getInstance()
        }
    }

fun TextView.addUnderline() {
    paint.flags = Paint.UNDERLINE_TEXT_FLAG
}

fun TextView.transparentHighlightColor() {
    highlightColor = Color.TRANSPARENT
}

fun TextView.startCountDown(
    lifecycleOwner: LifecycleOwner,
    secondInFuture: Int = 60,
    onTick: TextView.(secondUntilFinished: Int) -> Unit,
    onFinish: TextView.() -> Unit,
): CountDownTimer =
    object : CountDownTimer(secondInFuture * 1000L, 1000) {
        override fun onTick(millisUntilFinished: Long) {
            isEnabled = false
            onTick((millisUntilFinished / 1000f).roundToInt())
        }

        override fun onFinish() {
            isEnabled = true
            this@startCountDown.onFinish()
        }
    }.also { countDownTimer ->
        countDownTimer.start()
        lifecycleOwner.doOnLifecycle(onDestroy = {
            countDownTimer.cancel()
        })
    }

fun TextView.enableWhenOtherTextNotEmpty(vararg textViews: TextView) =
    enableWhenOtherTextChanged(*textViews) { all { it.isTextNotEmpty() } }

inline fun TextView.enableWhenOtherTextChanged(
    vararg textViews: TextView,
    crossinline block: Array<out TextView>.() -> Boolean
) {
    isEnabled = block(textViews)
    textViews.forEach { tv ->
        tv.doAfterTextChanged {
            isEnabled = block(textViews)
        }
    }
}

fun TextView.enableWhenAllChecked(vararg checkBoxes: CheckBox) {
    isEnabled = checkBoxes.all { it.isChecked }
    checkBoxes.forEach { cb ->
        cb.setOnCheckedChangeListener { _, _ ->
            isEnabled = checkBoxes.all { it.isChecked }
        }
    }
}

@OptIn(DelicateCoroutinesApi::class)
fun TextView.getTextLineCount(text: String, lineCount: (Int) -> (Unit)) {
    val params: PrecomputedTextCompat.Params = TextViewCompat.getTextMetricsParams(this)
    val ref: WeakReference<TextView>? = WeakReference(this)

    K.runOnScope (Dispatchers.Default) {
        val text = PrecomputedTextCompat.create(text, params)
        withContext(Dispatchers.Main) {
            ref?.get()?.let { textView ->
                TextViewCompat.setPrecomputedText(textView, text)
                lineCount.invoke(textView.lineCount)
            }
        }
    }
}

fun TextView.setRightDrawable(drawable: Drawable?,drawableSize: Size?=null) = TextViewExt.setCompoundDrawables(this,drawableSize,null,drawable)

fun TextView.setLeftDrawable(drawable: Drawable?,drawableSize: Size?=null) = TextViewExt.setCompoundDrawables(this,drawableSize,drawable,null)

fun TextView.setReadMoreText(text: CharSequence,readMoreConfig:AnotherReadMore.Builder.()->Unit){
    val builder=AnotherReadMore.Builder()
    readMoreConfig(builder)
    builder.build().addReadMoreTo(this,text)
}

class AnotherReadMore(
    private val textLength: Int = 0,
    private val textLengthType: Int = 0,
    private val moreLabel: String? = null,
    private val lessLabel: String? = null,
    private val moreLabelColor: Int,
    private val lessLabelColor: Int,
    private val underlineVisible: Boolean,
) {
    private constructor(builder: Builder) : this(
        builder.textLength,
        builder.textLengthType,
        builder.moreLabel,
        builder.lessLabel,
        builder.moreLabelColor,
        builder.lessLabelColor,
        builder.underlineVisible
    )

    fun addReadMoreTo(textView: TextView, text: CharSequence) {
        if (textLengthType == TYPE_CHARACTER) {
            if (text.length <= textLength) {
                textView.text = text
                return
            }
        } else {
            textView.setLines(textLength)
            textView.text = text
        }
        textView.post(Runnable {
            var textLengthNew = textLength
            if (textLengthType == TYPE_LINE) {
                if (textView.layout.lineCount <= textLength) {
                    textView.text = text
                    return@Runnable
                }
                val lp = textView.layoutParams as MarginLayoutParams
                val endLine = (textLength - 1).coerceAtMost(textView.layout.lineCount - 1);
                val subString = text.toString().substring(
                    textView.layout.getLineStart(0),
                    textView.layout.getLineEnd(endLine)
                )
                textLengthNew = subString.length - (moreLabel!!.length + 4 + lp.rightMargin / 6)
            }
            val spannableStringBuilder = SpannableStringBuilder(text.subSequence(0, textLengthNew))
                .append("...")
                .append(moreLabel)
            val ss = SpannableString.valueOf(spannableStringBuilder)

            val clickableSpan: ClickableSpan = CustomClickableSpan { addReadLess(textView, text) }

            ss.setSpan(
                clickableSpan,
                ss.length - moreLabel!!.length,
                ss.length,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
            )
            ss.setSpan(
                ForegroundColorSpan(moreLabelColor),
                ss.length - moreLabel.length,
                ss.length,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
            )
            if (underlineVisible) {
                ss.setSpan(
                    UnderlineSpan(),
                    ss.length - moreLabel.length,
                    ss.length,
                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                )
            }

            val layoutTransition = LayoutTransition()
            layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
            (textView.parent as ViewGroup).layoutTransition = layoutTransition
            textView.text = ss
            textView.movementMethod = LinkMovementMethod.getInstance()
        })
    }

    private fun addReadLess(textView: TextView, text: CharSequence) {
        textView.maxLines = Int.MAX_VALUE
        val spannableStringBuilder = SpannableStringBuilder(text)
            .append(lessLabel)
        val ss = SpannableString.valueOf(spannableStringBuilder)

        val clickableSpan: ClickableSpan = CustomClickableSpan { Handler().post { addReadMoreTo(textView, text) } }

        ss.setSpan(
            clickableSpan,
            ss.length - lessLabel!!.length,
            ss.length,
            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )
        ss.setSpan(
            ForegroundColorSpan(lessLabelColor),
            ss.length - lessLabel.length,
            ss.length,
            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )
        if (underlineVisible) {
            ss.setSpan(
                UnderlineSpan(),
                ss.length - lessLabel.length,
                ss.length,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
            )
        }

        textView.text = ss
        textView.movementMethod = LinkMovementMethod.getInstance()
    }

    class Builder() {

        var textLength = 100
            private set
        var textLengthType = TYPE_CHARACTER
            private set
        var moreLabel = "mais"
            private set
        var lessLabel = "menos"
            private set
        var moreLabelColor: Int = Color.BLACK
            private set
        var lessLabelColor: Int = Color.BLACK
            private set
        var underlineVisible: Boolean = true
            private set

        fun textLengthType(type: Int) = apply { textLengthType = type }
        fun moreLabel(more: String) = apply { moreLabel = more }
        fun lessLabel(less: String) = apply { lessLabel = less }
        fun moreLabelColor(color: Int) = apply { moreLabelColor = color }
        fun lessLabelColor(color: Int) = apply { lessLabelColor = color }
        fun underlineVisible(visible: Boolean) = apply { underlineVisible = visible }

        fun build() = AnotherReadMore(this)
    }

    companion object {
        const val TYPE_LINE = 1
        const val TYPE_CHARACTER = 2
    }

    class CustomClickableSpan(private val runnable: () -> Unit) : ClickableSpan() {

        override fun onClick(widget: View) {
            runnable()
        }

        override fun updateDrawState(ds: TextPaint) {
            super.updateDrawState(ds)
            ds.isUnderlineText = false
        }
    }
}

/**
 * Adds an underline effect to the text displayed in the TextView.
 */
fun TextView.addUnderline(start: Int = 0, end: Int = this.text.length, flags: Int = 0) {
    val currentText = text
    text = currentText.toSpannable().apply {
        setSpan(UnderlineSpan(), start, end, flags)
    }
}